#include "Marbre.hpp"

#include "Melange.hpp"

#include "Bruit/perlin.hpp"

#include <cmath>

#include <algorithm>

using namespace std;
using namespace boost::assign;

/**
 * Constructeur standard, permet d'initialiser les champs de la classe
 * mère.
 *
 * La version par défaut crée un motif à base de noir et de blanc.
 *                                                                             
 * @param refl coefficient de réflexion.
 * @param trans coefficient de transparence.
 * @param indice indice de réfraction.
 * @param reg coefficient de diffusion.
 * @param couleurs conteneur des instances de Couleur à utiliser.
 * @param coeff intensité de la perturbation.
 */
Marbre::Marbre(const double& refl, const double& trans,
	       const double& indice, const double& reg,
	       const vector< Couleur >& couleurs, const double& coeff)
  : EffetBase( refl, trans, indice, reg, couleurs, list_of( 0. ), coeff )
{
  Colors.resize( 2 );

  Type = tMarbre;
}

/**
 * Constructeur permetant d'initialiser les champs de la classe mère,
 * avec un bruit appliqué sur la normale.
 *                                                                             
 * @param refl coefficient de réflexion.
 * @param trans coefficient de transparence.
 * @param indice indice de réfraction.
 * @param reg coefficient de diffusion.
 * @param bruit intensité de la variation de la normale.
 * @param couleurs conteneur des instances de Couleur à utiliser.
 * @param coeff intensité de la perturbation.
 */
Marbre::Marbre(const double& refl, const double& trans,
	       const double& indice, const double& reg, const double& bruit,
	       const vector< Couleur >& couleurs,const double& coeff)	       
  : EffetBase( refl, trans, indice, reg, bruit, couleurs, list_of( 0. ), coeff )
{
  Colors.resize( 2 );

  Type = tMarbre;
}

/**
 * Le destructeur n'a rien de spécial à faire.
 */
Marbre::~Marbre()
{}

/**
 * Permet de retrouver la couleur de notre surface marbrée.
 *
 * On demande quelle est la valeur \f$\mathcal{B}\f$du bruit avec
 * valeurBruit, et on fait le mélange de nos deux couleurs avec des
 * poids \f$\mathcal{B}\f$ et \f$1 - \mathcal{B}\f$.
 *
 * @param pt point de la surface que l'on veut colorier.
 * @return une référence constante sur Color, qui est modifiée par la
 * fonction.
 */
Couleur Marbre::couleur(const Coord& pt)
{
  double val( valeurBruit( pt ) );

  vector< double > poids;

  poids = list_of( val )( 1 - val );
  
  return faireMelangeCouleurs( Colors, poids );
}

/**
 * Le bruit généré est ensuite transformé par une fonction donnant
 * très peu de maxima. La fonction utilisée est :
 *
 * \f[ 1 - \sqrt{ \left\vert \sin \left( \vec{x} \right) \right\vert^3
 * } ^{ \sqrt{ 1 - \sqrt{ \left\vert \sin \left( \vec{x} \right)
 * \right\vert } } } \f].
 *
 * @param pt point pour lequel on doit déterminer le bruit.
 * @return une valeur dans l'intervalle \f$\left[ 0, 1 \right]\f$,
 * générée avec la formule précédente.
 */
double Marbre::valeurBruit(const Coord& pt) const
{
  Coord p( pt );
  p *= Coefficient;
  double sqrtSin( sqrt( abs( sin( noise( p.x(), p.y(), p.z() ) ) ) ) );
  return 1 - pow( sqrtSin, 3. * ( 1 - sqrtSin ) );
}
